package http

import (
	adminV1 "open/api/open/admin/v1"
	v1 "open/api/open/service/v1"
	"open/internal/conf"
	"open/internal/service"
	"open/internal/service/admin"
	"open/internal/service/notify"
	_ "open/pkg/encoding/json"
	"open/pkg/log"
	"open/pkg/middleware/auth"
	"open/pkg/middleware/logging"
	"open/pkg/middleware/recovery"
	"open/pkg/middleware/validate"

	"github.com/go-kratos/kratos/v2/middleware"
	"github.com/go-kratos/kratos/v2/middleware/tracing"
	"github.com/go-kratos/kratos/v2/transport/http"
	"github.com/gorilla/handlers"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/propagation"
)

// NewServer new a HTTP server.
func NewServer(c *conf.Server, internal *service.Service, admin *admin.Service, notify *notify.Service) *http.Server {
	var opts []http.ServerOption
	if c.Http.Network != "" {
		opts = append(opts, http.Network(c.Http.Network))
	}
	if c.Http.Addr != "" {
		opts = append(opts, http.Address(c.Http.Addr))
	}
	if c.Http.Timeout != nil {
		opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
	}
	var mm []middleware.Middleware
	if tp := otel.GetTracerProvider(); tp != nil {
		mm = append(mm, tracing.Server(
			tracing.WithTracerProvider(tp),
			tracing.WithPropagators(
				propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{}),
			)))
	}
	mm = append(mm, []middleware.Middleware{
		logging.Server(),
		recovery.Recovery(),
		validate.Validator(),
	}...)
	opts = append(opts, http.Logger(log.NewZapLogger()))
	srv := http.NewServer(opts...)
	m := http.Middleware(
		middleware.Chain(
			mm...,
		),
	)
	authMm := append(mm, auth.Admin([]byte(c.Http.PublicKey)))
	authRequired := http.Middleware(
		middleware.Chain(
			authMm...,
		),
	)
	headersOk := handlers.AllowedHeaders([]string{
		"Accept",
		"Accept-Language",
		"Content-Language",
		"Origin",
		"Authorization",
		"Content-Type",
		"B-Token",
		"G-Token",
	})
	originsOk := handlers.AllowedOrigins([]string{"*"})
	methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
	// NOTE: 先后顺序很重要
	srv.HandleFunc("/ping", ping)

	srv.HandlePrefix("/v1/notify", NewNotifyHandler(notify, m))

	srv.HandlePrefix("/open/admin/v1", handlers.CORS(originsOk, headersOk, methodsOk)(adminV1.NewOpenAdminServiceHandler(admin, authRequired)))

	srv.HandlePrefix("/open/v1", handlers.CORS(originsOk, headersOk, methodsOk)(v1.NewOpenServiceHandler(internal, m)))
	return srv
}
